#ifndef CLIENTH
#define CLIENTH

#include <string>
#include <list>
#include <ctime>
#include <gmMachine.h>
#include <gmUserObject.h>
#include "sox.h"
#include "ByteStream.h"

#if defined(_MSC_VER)
#define		int32_t		__int32
#define		uint8_t		unsigned __int8
#define		uint32_t	unsigned __int32
#else
#include <stdint.h>
#endif

class Client
{
public:
    static const int PACKET_VERSIONOLD = 0;
    static const int PACKET_VERSIONNEW = 1;

private:
    class Codec
    {
    public:
        enum
        {
            ENCRYPT22_UNCOMPRESSED	= 0x02,
            ENCRYPT22_ZLIB			= 0x04,
            ENCRYPT22_BZ2			= 0x06
        };

        static const unsigned ITERATOR_START_VAL = 0x4A80B38;
        void Reset(uint8_t key);
        void Apply(uint8_t* buf, size_t len);
        void Limit(int32_t limit);
        int LimitFromType(uint8_t type);

    private:
        uint8_t  m_key;
        uint8_t  m_offset;
        uint32_t m_iterator;
        int32_t m_limit;
    };

    Codec           m_inCodec,
                    m_outCodec;

    int             m_packetVersion;
    time_t          m_lastPacketTime;

    sox::TcpSocket* m_socket;

    bool            m_deleteMe;

    ByteStream      m_recvStream,
                    m_sendStream,
                    m_sendStreamFinal;

    static ByteStream DecryptPacket(Client::Codec& codec, int codecType, const char* data, size_t dataLen);
    void CompressStream();
    std::string LogHeader() const {
        return "";
    }

public:
    Client(sox::TcpSocket* socket):
        m_socket(socket)
    {
        m_packetVersion = PACKET_VERSIONOLD;
        m_socket = socket;
        m_deleteMe = false;
        m_socket->DisableNagle();
        m_socket->Unblock();
        m_lastPacketTime = time(NULL);
    }

    ~Client(){
        m_socket->Close();
        delete m_socket;
    }

    void WritePacket(const ByteStream& packet);
    bool ReadPacket(ByteStream* stream);

    bool SendPackets();
    void RecvPackets();
    void Disconnect(const std::string& disMessage);
    void Disconnect();

    bool CanDelete() const {
        return m_deleteMe;
    }

    void UpdatePacketVersion(int key) {
        m_packetVersion = PACKET_VERSIONNEW;
        m_outCodec.Reset(key);
        m_inCodec.Reset(key);
    }

    sox::TcpSocket* GetSocket() {
        return m_socket;
    }

    bool CanSend() const {
        return m_sendStreamFinal.Length() < 0xFFFF;
    }

    int GetPacketVersion() const {
        return m_packetVersion;
    }

    size_t GetTimeout() const {
        return time(NULL) - m_lastPacketTime;
    }
};

class GMClient:
    public Client
{
private:
    gmUserObject* m_userObject;
    static gmType m_userType;

public:
    GMClient(gmMachine* machine, sox::TcpSocket* socket);
    ~GMClient();

    gmUserObject* GetUserObject() {
        return m_userObject;
    }

    static gmType GetType() {
        return m_userType;
    }

    static void Register(gmMachine* machine);
};

#endif // CLIENTH

